home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
BBS in a Box 7
/
BBS in a Box - Macintosh - Volume VII (BBS in a Box) (January 1993).iso
/
Files
/
Tele
/
C
/
Comet2.1.3.cpt
/
Comet
/
timer.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-07-26
|
12KB
|
504 lines
/* Copyright 1984 by the Massachusetts Institute of Technology */
/*
Copyright Cornell University 1986. All rights are reserved.
As of 4/10/86:
This source file may have no changes from the M.I.T original
other than this notice; but it has been tested as part of
Cornell's Aztec-C port. See notice.h
*/
/* See permission and disclaimer notice in file "notice.h" */
/* Addition of tm_tset and tm_mset, 12/83. <J.H. Saltzer> */
/* 8/19/86 kevin increased TIMERSTACK to 4000 so Alert() in timer
upcall won't cause crash */
/* 11/19/86 kevin decreased stack to 2000 */
/* 8/21/87 kevin decreased stack to 1000 */
/* 10/16/87 kevin changed q_adda to q_addb so timers really are
in timeout order */
/* 10/16/87 kevin made timer q traversals more intelligible */
/* 3/16/88 kevin removed register declarations which caused bugs */
/* 3/16/88 kevin made sure tnonce never == 0 */
/* 7/24/89 kevin rewrote for MacTCP, added alarm.c */
#include <notice.h>
/* This file contains the routines which make up the timer task and
* its associated subroutines for setting and clearing timers. The
* timer task manages a queue of timers, each of which specifies a
* subroutine to be called (in the context of the timer task) when the
* timer goes off.
* The following routines are included in this package:
* tm_set set a timer to fire after number of seconds
* tm_mset set a timer, argument in milliseconds
* tm_tset set a timer, argument in clock ticks
* tm_reset reset a timer to go off at a different time
* tm_clear clear a previously set timer
* tm_main the main routine of the timer task
* tm_init init the timer system
* tm_off turn off timer interrupts
* tm_on turn timer interrupts back on
*/
#include <q.h>
#include <timer.h>
#include <retrace.h>
#include <memory.h>
#include <emdefs.h>
extern char *malloc();
#define TIMERHIWATER 30 /* number of free timers to keep */
time_q tm_queue; /* queue of active timers */
time_q tm_freeq; /* queue of free timers */
nonce tnonce; /* timer nonce generator */
char timertripped = FALSE; /* the timer requires service */
unsigned long cticks = 0L;
unsigned long next_alarm = 0L;
unsigned long oldalarm;
unsigned TIMERDEBUG = 0;
/* set timer in seconds */
tm_set(nsecs, subr, arg, tm)
int nsecs; /* timer expiration time, in secs */
int (*subr)(); /* subroutine to call on expiration */
char *arg; /* arg to pass to subr. */
timer *tm; /* place to return timer id */
{
tm_tset(nsecs*TPS, subr, arg, tm);
}
/* set timer in milliseconds */
tm_mset(msecs, subr, arg, tm)
long msecs; /* timer expiration time, in msecs */
int (*subr)(); /* subroutine to call on expiration */
char *arg; /* arg to pass to subr. */
timer *tm; /* place to return timer id */
{
tm_tset((int)((msecs*TPS)/1000), subr, arg, tm);
}
/* Set a timer to go off after nticks clock ticks. When the timer
goes off, call the specified subroutine with the specified
argument flag. This routine enqueues the timer and,
if it is the first timer
on the queue, sets an flag to call the timer task.
*/
tm_tset(nticks, subr, arg, tm)
int nticks; /* timer expiration time */
int (*subr)(); /* subroutine to call on expiration */
char *arg; /* arg to pass to subr. */
timer *tm; /* place to return timer id */
{
timer *tmp; /* temp for chaining */
queue *qp;
#ifdef DEBUG
if (TIMERDEBUG) {
printf("TM_SET: %8X set for %u ticks\n", tm, nticks);
}
#endif
if (tm == (timer *) NULL)
return(-1);
q_del(&tm_queue, tm); /* make sure not already queued. */
tm->tm_elt.qe_next = NULL; /* no next element */
tm->tm_time = cticks + nticks; /* timer expiration time */
/* TODO should check for wrap */
if (!++tnonce)
/* make sure it skips zero */
++tnonce;
tm->tm_nonce = tnonce; /* nonce for this timer */
tm->tm_subr = subr; /* subroutine to call */
tm->tm_arg = arg; /* argument to pass */
/* add to queue in timeout order */
for (tmp = tm_queue.tmq_head;
tmp != NULL && tmp->tm_time <= tm->tm_time;
tmp = (timer *) tmp->tm_elt.qe_next)
;
qp = (queue *) &tm_queue;
q_addb(qp, (q_elt) tmp, (q_elt) tm);
if (tm_queue.tmq_head == tm) /* first elt in queue? */
set_alarm(nticks); /* yes, wake up timer task then */
}
/* Reset a (running) timer to go off in nsecs seconds
instead of at the time it is currently set for. If in fact the
timer is not already set, return FALSE; otherwise return TRUE.
Does not modify the upcall in the timer.
*/
tm_reset(nsecs, tm)
int nsecs; /* timer expiration time in seconds */
timer *tm; /* timer id to reset*/
{
timer *tmp; /* temp for chaining */
queue *qp;
if (tm->tm_nonce == 0 || ! q_del(&tm_queue, tm)) {
#ifdef DEBUG
if (TIMERDEBUG)
printf("TM_RST: %8X already expired.\n", tm);
#endif
return(FALSE); /* timer expired, give up */
}
#ifdef DEBUG
if (TIMERDEBUG)
printf("TM_RST: %8X reset for %u seconds.\n", tm, nsecs);
#endif
tm->tm_elt.qe_next = NULL; /* no next element */
tm->tm_time = cticks + nsecs * TPS; /* timer expiration time */
for (tmp = tm_queue.tmq_head;
tmp != NULL && tmp->tm_time <= tm->tm_time;
tmp = (timer *) tmp->tm_elt.qe_next)
/* add to queue in timeout order */
;
qp = (queue *)&tm_queue;
q_addb(qp, (q_elt)tmp, (q_elt)tm);
if (tm_queue.tmq_head == tm) /* first elt in queue? */
set_alarm(nsecs*TPS); /* yes, wake up timer task then */
return(TRUE);
}
/* Clear the timer specified by the passed timer identifier. The
timer identifier gives a pointer to the timer to be cleared.
Free the timer's storage
(into the free list up to TIMERHIWATER elements).
If it was the only timer on the queue, reset the pending alarm.
Returns FALSE if the specified alarm was not found in the queue
TRUE otherwise.
*/
tm_clear(tm)
timer *tm; /* identifier of timer to clear */
{
if (tm->tm_nonce == 0) {
#ifdef DEBUG
if (TIMERDEBUG)
printf("TM_CLR: %8X already expired.\n", tm);
#endif
return FALSE;
}
#ifdef DEBUG
if (TIMERDEBUG)
printf("TM_CLR: %8X\n", tm);
#endif
tm->tm_nonce = 0;
if(!q_del(&tm_queue, tm))
return(FALSE); /* timer expired, give up */
if (tm_queue.tmq_head == NULL) /* last elt on queue? */
set_alarm(-1); /* yes, turn off alarm */
return(TRUE); /* success */
}
/* This routine forms the body of the timer management task. Its
job is simply to dequeue expired timers from the timer queue
and call the subroutine specified therein, passing it the argument
specified therein.
Note that this task needs the alarm signal, so all timer
management in the process must be via this task - no one else
may use the alarm() calls!
*/
tm_main()
{
timer *tm_tmp; /* temp for holding timer */
timertripped = FALSE;
while ((tm_tmp = tm_queue.tmq_head) != NULL
&& cticks >= tm_tmp->tm_time) {
/* for all expired timers */
tm_tmp = (timer *) q_deq(&tm_queue); /* dequeue it */
/* if the timer is expired, ignore it */
if (tm_tmp->tm_nonce == 0) {
#ifdef DEBUG
if (TIMERDEBUG)
printf(" TIMER: %8X already fired.\n", tm_tmp);
#endif
continue;
}
#ifdef DEBUG
if (TIMERDEBUG)
printf(" TIMER: %8X firing.\n", tm_tmp);
#endif
tm_tmp->tm_nonce = 0; /* show timer expired... */
(*tm_tmp->tm_subr)(tm_tmp->tm_arg); /* call its routine */
}
if (tm_queue.tmq_head != NULL) {
/* there are outstanding timers, reset the alarm */
set_alarm((int)(tm_queue.tmq_head->tm_time - cticks));
}
}
/* Initialize the timer package. Set up the alarm routine tm_signal(). */
tm_init()
{
extern int alarm_clnup();
alarm_init(); /* set up out alarm function */
exit_hook(alarm_clnup);
}
/* Turn off timer interrupts. This is useful while writing data to
the terminal. */
tm_off()
{
oldalarm = next_alarm;
set_alarm(-1);
}
/* Turn timer interrupts back on. This is done by running the
timer task to process any events that went off while timer
interrupts were disabled. If anything else is queued, that task
will set a new alarm. */
tm_on()
{
tm_main();
}
/*****/
timer *tm_alloc()
{
/* Allocate a timer and return a pointer to it */
timer *t;
if((t = (timer *)q_deq(&tm_freeq)) == NULL &&
(t = (timer *)malloc(sizeof(timer))) == NULL) return NULL;
t->tm_elt.qe_next = NULL;
return t;
}
/*****/
tm_free(t)
timer *t;
{
/* Free up a timer. Returns true if successful, false otherwise */
timer **tmp;
queue *qp;
/* Check if the timer is enqueued */
for(tmp = &tm_queue.tmq_head; *tmp != NULL;
tmp = (timer **)(((timer *)tmp)->tm_elt.qe_next))
if(*tmp == t) {
#ifdef DEBUG
printf("Tried to free active timer.\n");
#endif
return FALSE;
}
if (tm_freeq.tmq_len < TIMERHIWATER)
{
qp = (queue *)&tm_freeq;
q_addh(qp, (q_elt)t);
}
/*
else
cfree(t);
*/
return TRUE;
}
/*****/
tm_qdump()
{
/*
Dump some information about the timer queue to the display. Used
for debugging. Takes a timer * as an argument and tells if it's
in the queue.
*/
printf("Nonce = %u\n", tnonce);
printf("timer queue:\n");
printf("\thead %8X\ttail %8X\n", tm_queue.tmq_head,
tm_queue.tmq_tail);
printf("\tlength %u\n", tm_queue.tmq_len);
}
/*****/
tm_dump(t)
timer *t;
{
/* For debugging, dump the interesting parts of a timer. */
printf("dump of timer %8X:\n", t);
printf(" fire time is %8X, nonce is %u, next q element is %8X\n",
t->tm_time, t->tm_nonce, t->tm_elt.qe_next);
}
/* from task/alarm.c */
/*
Copyright 1984 M.I.T.
Copyright Cornell University 1986. All rights are reserved.
As of 4/10/86:
This source file may have no changes from the M.I.T original
other than this notice; but it has been tested as part of
Cornell's Aztec-C port. See notice.h
*/
/* 5/8/87 kevin modified to remove saveA5() and avoid a5 problems john discovered */
/* 4/13/88 kevin modified for MultiFinder: allocated VBL task in system heap */
#define vType 1
struct a5save {
char * mya5; /* saves the a5 */
VBLTask mytask;
} myvbl;
alarm_init()
{
void signal();
short status;
THz appzone;
appzone = GetZone();
SetZone(SystemZone());
/* allocate the alarm in the system heap
so that it gets service even when MultiFinder
switches out application */
myvbl.mytask.qType = vType;
myvbl.mytask.vblAddr = signal;
myvbl.mytask.vblCount = 1; /* call every tick */
myvbl.mytask.vblPhase = 0;
myvbl.mya5 = (char *) (* (long *) 0x904);
status = VInstall(&myvbl.mytask);
SetZone(appzone);
if (status) {
error("Can't install retrace routine");
return(-1);
}
return(0);
}
alarm_clnup()
{
short status;
THz appzone;
appzone = GetZone();
SetZone(SystemZone());
status = VRemove(&myvbl.mytask);
SetZone(appzone);
}
/* this routine is called by the VBL timer every tick */
void signal()
{
#asm
move.l a5,-(a7)
movea.l -4(a0),a5
; move the a5 we saved earlier into a5
move.w #1,10(a0)
; task->vblCount = 1; doesn't work on a Mac Portable!
#endasm
cticks++;
if (!cticks) {
/* we've wrapped around, reset times on q that have popped */
tm_wrap();
user_tmwrap();
}
if (next_alarm)
/* don't decrement if already zero */
if (--next_alarm == 0L)
timertripped = TRUE;
; /* null statement so asm directive gets spotted! */
#asm
move.l (a7)+,a5
#endasm
}
set_alarm(ntime)
int ntime;
{
if (ntime >= -1) {
/* ignore weird args */
next_alarm = ntime + 1;
}
}
tm_wrap()
{
timer *timerp;
for (timerp = tm_queue.tmq_head; timerp != NULL;
timerp = (timer *) timerp->tm_elt.qe_next) {
/* if the time value is large, reset it so the timer will service it */
if (timerp->tm_nonce) {
if (timerp->tm_time > 3000000)
timerp->tm_time = 0;
}
}
}